{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# A Replay of US Presidential Election outcomes: calculating a portfolio's sensitivity to a Democratic or Republican presidential election win.\n",
    "\n",
    "In this example, we will create custom factor shocks scenarios that showcase the potential impact of a Democratic or\n",
    "Republican presidential election win on the performance of your portfolio. The objective is to build custom factor shocks\n",
    "that encapsulate equity factors' performance during each US presidential election outcome. These factor shocks will be\n",
    "the average of factor moves on each outcome over the previous 6 presidential elections (2000, 2004, 2008, 2012, 2016, 2020).\n",
    "\n",
    "These are the steps to build the factor shocks:\n",
    "1. Identify the time period and the outcome of each presidential election for the previous 6 elections\n",
    "2. Select a risk model and capture the market conditions one week post the election results announcement, which entails pulling and aggregating realized factor returns over the one-week period post the presidential election results announcement.\n",
    "3. The final factor shocks are an average of factor moves on each election outcome.\n",
    "\n",
    "## Step1: Select the risk model\n",
    "\n",
    "Select a risk model. See [full list of available risk models here](https://marquee.gs.com/s/discover/data-services/catalog?Category=Factor+Risk+Model)."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from gs_quant.session import GsSession, Environment\n",
    "from gs_quant.markets.scenario import FactorScenario, FactorShock, FactorShockParameters, FactorScenarioType\n",
    "from gs_quant.markets.portfolio_manager import PortfolioManager, ScenarioCalculationMeasure\n",
    "from gs_quant.models.risk_model import FactorRiskModel\n",
    "import datetime as dt\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from time import sleep\n",
    "\n",
    "GsSession.use(Environment.PROD)\n",
    "\n",
    "risk_model_id = \"BARRA_USFAST\"\n",
    "risk_model = FactorRiskModel.get(risk_model_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Step 2: Identify time periods for each election outcome.\n",
    "\n",
    "Democrats won elections in 2008, 2012, and 2020 whereas Republicans won elections in 2000, 2004 and 2016\n",
    "\n",
    "1. **Nov 7, 2000 to Nov 14, 2000**: Republican Party win\n",
    "2. **Nov 2, 2004 ot Nov 9, 2004**: Republican Party win\n",
    "3. **Nov 4, 2008 to Nov 11, 2008**: Democratic Party win\n",
    "4. **Nov 6, 2012 to Nov 13, 2012**: Democratic Party win\n",
    "5. **Nov 8, 2016 to Nov 15, 2016**: Republican Party win\n",
    "6. **Nov 3, 2020 to Nov 10, 2020**: Democratic Party win."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "outputs": [],
   "source": [
    "presidential_elections_2000 = ((dt.date(2000, 11, 7)), dt.date(2000, 11, 14))\n",
    "presidential_elections_2004 =  ((dt.date(2004, 11, 2)), dt.date(2004, 11, 9))\n",
    "presidential_elections_2008 = ((dt.date(2008, 11, 4)), dt.date(2008, 11, 11))\n",
    "presidential_elections_2012 = ((dt.date(2012, 11, 6)), dt.date(2012, 11, 13))\n",
    "presidential_elections_2016 = ((dt.date(2016, 11, 8)), dt.date(2016, 11, 15))\n",
    "presidential_elections_2020 = ((dt.date(2020, 11, 3)), dt.date(2020, 11, 10))\n",
    "\n",
    "democrats_win_dates = [presidential_elections_2008, presidential_elections_2012, presidential_elections_2020]\n",
    "republicans_win_dates = [presidential_elections_2000, presidential_elections_2004, presidential_elections_2016]"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Step 3: What were the factor moves in the week following US presidential elections?\n",
    "\n",
    "We then pull realized daily factor returns for each one-week election period and aggregate them to get factor performance over the period."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "outputs": [],
   "source": [
    "election_period_to_factor_returns_map = {}\n",
    "for period in democrats_win_dates + republicans_win_dates:\n",
    "    start, end = period\n",
    "    factor_returns = risk_model.get_factor_returns_by_name(start, end)\n",
    "\n",
    "    factor_returns.index = pd.to_datetime(factor_returns.index.values, format=\"%Y-%m-%d\")\n",
    "    factor_returns = factor_returns.sort_index()\n",
    "\n",
    "    # aggregate returns\n",
    "    returns_geometrically_aggregated = np.multiply.reduce(1 + factor_returns.values / 100) - 1\n",
    "    factor_shocks_df = pd.DataFrame(returns_geometrically_aggregated,\n",
    "                                    index=factor_returns.columns.values,\n",
    "                                    columns=[\"factorShocks\"])\n",
    "\n",
    "    election_period_to_factor_returns_map[period] = factor_shocks_df"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Step 4: Build the factor shocks.\n",
    "\n",
    "The final factor shocks will be the average of the performance of factors for all periods."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "outputs": [],
   "source": [
    "factor_shocks_democratic_win_df = pd.concat([election_period_to_factor_returns_map.get(period) for period in democrats_win_dates], axis=1)\n",
    "factor_shocks_republican_win_df = pd.concat([election_period_to_factor_returns_map.get(period) for period in republicans_win_dates], axis=1)\n",
    "\n",
    "number_of_democratic_wins = factor_shocks_democratic_win_df.columns.size\n",
    "number_of_republican_wins = factor_shocks_republican_win_df.columns.size\n",
    "\n",
    "# Filter factors that have been demised (your portfolio won't have exposures to them anyway)\n",
    "available_factors = risk_model.get_factor_data()\n",
    "\n",
    "\n",
    "unavailable_factors_dem_wins = set(factor_shocks_democratic_win_df.index.tolist()) - set(available_factors['name'].tolist())\n",
    "factor_shocks_democratic_win_df = factor_shocks_democratic_win_df.drop(index=list(unavailable_factors_dem_wins))\n",
    "\n",
    "unavailable_factors_rep_wins = set(factor_shocks_republican_win_df.index.tolist()) - set(available_factors['name'].tolist())\n",
    "factor_shocks_republican_win_df = factor_shocks_republican_win_df.drop(index=list(unavailable_factors_rep_wins))\n",
    "\n",
    "factor_shocks_democratic_win_df = (factor_shocks_democratic_win_df.apply(np.sum, axis=1).to_frame() / number_of_democratic_wins) * 100\n",
    "factor_shocks_republican_win_df = (factor_shocks_republican_win_df.apply(np.sum, axis=1).to_frame() / number_of_republican_wins) * 100"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Step 4: Create the custom shock scenario\n",
    "\n",
    "Once we have factor shocks, it is time to create the custom shock scenario. We will have:\n",
    "\n",
    "1. A `FactorShockParameters` which comprises the factor shocks constructed above as well as the risk model used and whether we are propagating shocks.\n",
    "2. The type of the scenario with is `FactorScenarioType.Factor_Shock`\n",
    "3. Other scenario metadata such as name and description"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "outputs": [],
   "source": [
    "dems_shocks_map = factor_shocks_democratic_win_df.to_dict(orient=\"split\")\n",
    "\n",
    "rep_shocks_map = factor_shocks_republican_win_df.to_dict(orient=\"split\")\n",
    "\n",
    "final_factor_shocks_democratic_wins = [FactorShock(factor=f, shock=v)\n",
    "                                       for f, v in dict(zip(dems_shocks_map.get('index'), [s[0] for s in dems_shocks_map.get('data')])).items()]\n",
    "final_factor_shocks_republican_wins = [FactorShock(factor=f, shock=v)\n",
    "                                       for f, v in dict(zip(rep_shocks_map.get('index'), [s[0] for s in rep_shocks_map.get('data')])).items() ]\n",
    "\n",
    "# Since we are shocking every single factor; no need to propagate shocks\n",
    "parameters_democratic_win = FactorShockParameters(risk_model=risk_model_id,\n",
    "                                                   propagate_shocks=False,\n",
    "                                                   factor_shocks=final_factor_shocks_democratic_wins)\n",
    "\n",
    "\n",
    "parameters_republican_win = FactorShockParameters(risk_model=risk_model_id,\n",
    "                                                  propagate_shocks=False,\n",
    "                                                  factor_shocks=final_factor_shocks_republican_wins)\n",
    "\n",
    "democratic_election_win_scenario = FactorScenario(name=\"Democratic Presidential Wins 1 week after (2008, 2012, 2020)\",\n",
    "                                             description=f\"Factor moves in {risk_model_id} one week post Democratic presidential election win (2008, 2012, 2020). Factor shocks are an average of aggregate factor returns on each period\",\n",
    "                                             type=FactorScenarioType.Factor_Shock,\n",
    "                                             parameters=parameters_democratic_win,\n",
    "                                             tags=[\"Presidential Election\"])\n",
    "\n",
    "democratic_election_win_scenario.save()\n",
    "republican_election_win_scenario = FactorScenario(name=\"Republican Presidential Wins 1 week after (2000, 2004, 2016)\",\n",
    "                                             description=f\"Factor moves in {risk_model_id} one week post Republican presidential election win (2000, 2004, 2016). Factor shocks are an average of aggregate factor returns on each period\",\n",
    "                                             type=FactorScenarioType.Factor_Shock,\n",
    "                                             parameters=parameters_republican_win,\n",
    "                                             tags=[\"Presidential Election\"])\n",
    "\n",
    "republican_election_win_scenario.save()\n",
    "\n",
    "sleep(2)\n"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Step 5: Run both election outcome stress tests on your portfolio to observe estimated PnL impact\n",
    "\n",
    "To run the scenarios on a portfolio, we will need the following:\n",
    "\n",
    "- Scenarios: The Democratic election win and Republican election win scenarios\n",
    "- A date: date on which to run the two scenarios. By default, the date is the latest date with risk analytics are available.\n",
    "- Risk Model: The selected risk model.\n",
    "- Measures: Metrics to return, which include:\n",
    "    1. Total portfolio estimated factor PnL\n",
    "    2. Total portfolio PnL by GICS sector & industry\n",
    "    3. Total portfolio PnL by region\n",
    "    4. Total portfolio PnL by the direction (LONG/SHORT) of your portfolio holdings,\n",
    "    5. Estimated performance of each asset in your portfolio."
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "outputs": [],
   "source": [
    "portfolio_id = \"PORTFOLIO ID\"\n",
    "pm = PortfolioManager(portfolio_id)\n",
    "risk_report_latest_end_date = pm.get_factor_risk_report(risk_model_id).latest_end_date\n",
    "election_outcome_scenario_analytics = pm.get_factor_scenario_analytics(scenarios=[republican_election_win_scenario,\n",
    "                                                                                  democratic_election_win_scenario],\n",
    "                                                                       date=risk_report_latest_end_date,\n",
    "                                                                       measures=[ScenarioCalculationMeasure.SUMMARY,\n",
    "                                                                                 ScenarioCalculationMeasure.ESTIMATED_FACTOR_PNL,\n",
    "                                                                                 ScenarioCalculationMeasure.ESTIMATED_PNL_BY_SECTOR,\n",
    "                                                                                 ScenarioCalculationMeasure.ESTIMATED_PNL_BY_REGION,\n",
    "                                                                                 ScenarioCalculationMeasure.ESTIMATED_PNL_BY_DIRECTION],\n",
    "                                                                       risk_model=risk_model_id)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}